#include "pebble_os.h"
#include "pebble_app.h"
#include "pebble_fonts.h"
#include "command.h"
#include "menu_modes.h"
#include "menu_video_resolution_modes.h"
#include "menu_photo_resolution_modes.h"
#include "menu_photo_burst_option.h"
#include "menu_timelapse.h"
#include "menu_fps.h"
#include "menu_continuous_photo.h"


#define MY_UUID { 0xA0, 0x9C, 0x5E, 0x34, 0x44, 0x5C, 0x4A, 0xC2, 0xB6, 0x94, 0xF2, 0x0C, 0x06, 0x4D, 0x83, 0x32 }
PBL_APP_INFO(MY_UUID,
             "GoPro", "Grégoire Sage",
             1, 2, /* App version */
             RESOURCE_ID_IMAGE_ICON,
             APP_INFO_STANDARD_APP);

typedef enum {
    OFF   = 0x00,
    IDLE  = 0x01,
    ON    = 0x02,
} PowerStatus;

Window window;
Layer layer;
BmpContainer image;

GFont small_font;
GFont big_font;

PowerStatus powerstatus = OFF;

static char main_text[10] = "";
static char remaining_text[10] = "";
static char info_text[20] = "";
static char info_text_2[20] = "";

static void displayFOV(GContext* ctx) {
  switch(data[7]){
    case 0 : graphics_text_draw(ctx, "W", small_font, GRect(60, 5, 30, 30), GTextOverflowModeWordWrap,GTextAlignmentLeft,NULL); break;
    case 1 : graphics_text_draw(ctx, "M", small_font, GRect(60, 5, 30, 30), GTextOverflowModeWordWrap,GTextAlignmentLeft,NULL); break;
    case 2 : graphics_text_draw(ctx, "N", small_font, GRect(60, 5, 30, 30), GTextOverflowModeWordWrap,GTextAlignmentLeft,NULL); break;
  }
}

static void displayVideo(GContext* ctx) {

  bmp_init_container(RESOURCE_ID_IMAGE_MINI_VIDEO, &image);

  //if recording
  if(data[29]){
    // display the recording minutes/seconds
    snprintf(main_text, sizeof(main_text), "%02d:%02d", data[13],data[14]); 
  }
  else {
    // display the video count
    snprintf(main_text, sizeof(main_text), "%02d", ((data[27] << 8) | data[28])); 
  }

  // info text
  snprintf(info_text, sizeof(info_text), "%s", get_video_resolution_modes_str()); 
  // protune ?
  if(((data[30] >> 1) & 1)){
    snprintf(info_text_2, sizeof(info_text_2), "%sT", get_fps_str()); 
  }
  else {
    snprintf(info_text_2, sizeof(info_text_2), "%s", get_fps_str()); 
  }

  // display the remaining video time
  int minutes = ((data[25] << 8) | data[26]);
  int hours = minutes / 60;
  snprintf(remaining_text, sizeof(remaining_text), "%02dH%02d", hours, minutes - hours * 60); 

  displayFOV(ctx);
}

static void displayPhoto(GContext* ctx) {

  bmp_init_container(RESOURCE_ID_IMAGE_MINI_PHOTO, &image);

  // display the photo count
  snprintf(main_text, sizeof(main_text), "%02d", ((data[23] << 8) | data[24])); 

  // info text
  snprintf(info_text, sizeof(info_text), "%s", get_photo_resolution_modes_str());
  snprintf(info_text_2, sizeof(info_text_2), "%s", get_continuous_photo_str());

  // display the remaining photo count
  int photos = ((data[21] << 8) | data[22]);
  snprintf(remaining_text, sizeof(remaining_text), "%02d", photos); 
}

static void displayBurst(GContext* ctx) {

  bmp_init_container(RESOURCE_ID_IMAGE_MINI_BURST, &image);

  // display the photo count
  snprintf(main_text, sizeof(main_text), "%02d", ((data[23] << 8) | data[24])); 

  // info text
  snprintf(info_text, sizeof(info_text), "%s", get_photo_resolution_modes_str());
  snprintf(info_text_2, sizeof(info_text_2), "%s", get_photo_burst_option_str());

  // display the remaining photo count
  int photos = ((data[21] << 8) | data[22]);
  snprintf(remaining_text, sizeof(remaining_text), "%02d", photos); 
}

static void displayTimelapse(GContext* ctx) {

  bmp_init_container(RESOURCE_ID_IMAGE_MINI_TIMELAPSE, &image);

  // display the photo count
  snprintf(main_text, sizeof(main_text), "%02d", ((data[23] << 8) | data[24])); 

  // info text
  snprintf(info_text, sizeof(info_text), "%s", get_photo_resolution_modes_str());
  snprintf(info_text_2, sizeof(info_text_2), "%s", get_timelapse_str());

  // display the remaining photo count
  int photos = ((data[21] << 8) | data[22]);
  snprintf(remaining_text, sizeof(remaining_text), "%02d", photos); 
}

static void displayBattery(GContext* ctx) {
  graphics_context_set_fill_color(ctx, GColorWhite);
  graphics_fill_rect(ctx,GRect(0, 117, 160, 3),0,0);

  graphics_context_set_fill_color(ctx, GColorWhite);
  graphics_fill_rect(ctx,GRect(94, 134, 3, 5),0,0);
  graphics_fill_rect(ctx,GRect(97, 128, 39, 17),0,0);
  graphics_context_set_fill_color(ctx, GColorBlack);
  graphics_fill_rect(ctx,GRect(99, 130, 35, 13),0,0);

  graphics_context_set_fill_color(ctx, GColorWhite);
  switch(data[19]){
    case 4 : 
      graphics_draw_line ( ctx, GPoint(101, 132), GPoint(101 + 16, 132 + 9) );
      graphics_draw_line ( ctx, GPoint(101 + 16, 132 + 9), GPoint(101 + 16, 132 + 5) );
      graphics_draw_line ( ctx, GPoint(101 + 16, 132 + 5), GPoint(101+31, 132+9) );
      graphics_draw_line ( ctx, GPoint(101+31, 132+9), GPoint(101 + 14, 132) );
      graphics_draw_line ( ctx, GPoint(101 + 14, 132), GPoint(101 + 14, 132 + 4) );
      graphics_draw_line ( ctx, GPoint(101 + 14, 132 + 4), GPoint(101, 132) );
      break;
    case 3 : graphics_fill_rect(ctx,GRect(101, 132, 9, 9),0,0);
    case 2 : graphics_fill_rect(ctx,GRect(112, 132, 9, 9),0,0);
    case 1 : graphics_fill_rect(ctx,GRect(123, 132, 9, 9),0,0);
  }
}

static void layer_update_callback(Layer *me, GContext* ctx) {

  if(powerstatus == OFF){
    graphics_text_draw(ctx,
      "GoPro not connected",
      small_font,
      GRect(5, 50, 144 - 10, 100),
      GTextOverflowModeWordWrap,
      GTextAlignmentCenter,
      NULL);
    return;
  }
  else if(powerstatus == IDLE){
    graphics_text_draw(ctx,
      "GoPro is off\n\nPress select to start",
      small_font,
      GRect(5, 30, 144 - 10, 100),
      GTextOverflowModeWordWrap,
      GTextAlignmentCenter,
      NULL);
    return;
  }

  bmp_deinit_container(&image);

  switch(data[1]) {
    case 1 : displayPhoto(ctx); break;
    case 2 : displayBurst(ctx); break;
    case 3 : displayTimelapse(ctx); break;
    default : displayVideo(ctx); break;
  }

  GRect destination = layer_get_frame(&image.layer.layer);

  destination.origin.y = 5;
  destination.origin.x = 10;

  graphics_draw_bitmap_in_rect(ctx, &image.bmp, destination);

  graphics_text_draw(ctx,
      info_text,
      small_font,
      GRect(10, 36, 70, 30),
      GTextOverflowModeWordWrap,
      GTextAlignmentLeft,
      NULL);
  graphics_text_draw(ctx,
      info_text_2,
      small_font,
      GRect(72, 36, 62, 30),
      GTextOverflowModeWordWrap,
      GTextAlignmentRight,
      NULL);

  graphics_text_draw(ctx,
      main_text,
      big_font,
      GRect(10, 62, 140, 50),
      GTextOverflowModeWordWrap,
      GTextAlignmentLeft,
      NULL);

  graphics_text_draw(ctx,
      remaining_text,
      small_font,
      GRect(10, 124, 80, 30),
      GTextOverflowModeWordWrap,
      GTextAlignmentLeft,
      NULL);

  displayBattery(ctx);
}


static void select_single_click_handler(ClickRecognizerRef recognizer, Window *window) {
  (void)recognizer;
  (void)window;

  if(powerstatus == OFF)
    return;
  else if(powerstatus == IDLE){
    // turn on the gopro
    send_bacpac_cmd("PW", 0x01); 
    return;
  }
  else if(powerstatus == ON){
    switch(data[1]) {
      case 0 : //video
      case 3 : //timelapse
          //if recording
        if(data[29])
          send_bacpac_cmd("SH", 0x00); 
        else 
          send_bacpac_cmd("SH", 0x01); 
        break;
      case 1 : //photo
      case 2 : //burst
        send_bacpac_cmd("SH", 0x01); 
        break;
    }
  }
}


static void up_down_single_click_handler(ClickRecognizerRef recognizer, Window *window) {
  (void)recognizer;
  (void)window;

  if(powerstatus != ON)
    return;

  switch(data[1]) {
    case 0 : show_menu_modes(0); break;
    case 1 : show_menu_modes(1); break;
    case 2 : show_menu_modes(2); break;
    case 3 : show_menu_modes(3); break;
    default: show_menu_modes(0); break;
  }
  
}

static void config_provider(ClickConfig **config, Window *window) {
  (void)window;

  config[BUTTON_ID_SELECT]->click.handler = (ClickHandler) select_single_click_handler;
  config[BUTTON_ID_SELECT]->click.repeat_interval_ms = 100;

  config[BUTTON_ID_UP]->click.handler = (ClickHandler) up_down_single_click_handler;
  config[BUTTON_ID_UP]->click.repeat_interval_ms = 100;

  config[BUTTON_ID_DOWN]->click.handler = (ClickHandler) up_down_single_click_handler;
  config[BUTTON_ID_DOWN]->click.repeat_interval_ms = 100;
}

static void window_appear(Window* window) {
     layer_mark_dirty(&layer);
}

static void handle_init(AppContextRef ctx) {
  (void)ctx;

  // Reduce the sniff interval for more responsive messaging at the expense of
  // increased energy consumption by the Bluetooth module
  // The sniff interval will be restored by the system after the app has been
  // unloaded
  app_comm_set_sniff_interval(SNIFF_INTERVAL_REDUCED);

  window_init(&window, "GoPro");
  window_set_window_handlers(&window, (WindowHandlers){
        .appear = window_appear,
    });
  window_set_background_color(&window, GColorBlack); 

  resource_init_current_app(&GOPRO_RESOURCES); 

  small_font = fonts_load_custom_font(resource_get_handle(RESOURCE_ID_FONT_COMFORTAA_20));
  big_font = fonts_load_custom_font(resource_get_handle(RESOURCE_ID_FONT_COMFORTAA_40));

  // Attach our desired button functionality
  window_set_click_config_provider(&window, (ClickConfigProvider) config_provider);

  // Init the layer for the minute display
  layer_init(&layer, window.layer.frame);
  layer.update_proc = &layer_update_callback;
  layer_add_child(&window.layer, &layer);

  // init the image
  bmp_init_container(RESOURCE_ID_IMAGE_MINI_VIDEO, &image);

  window_stack_push(&window, true);
}

static void handle_deinit(AppContextRef ctx) {
  (void)ctx;

  fonts_unload_custom_font(small_font);
  fonts_unload_custom_font(big_font);

  bmp_deinit_container(&image);
}

static void in_received_handler(DictionaryIterator *iter, void *context) {
  Tuple *data_tuple = dict_find(iter, GOPRO_STATUS_KEY);
  if (data_tuple) {
    if(memcmp(&data,&data_tuple->value->uint8,56) != 0){
      memcpy(&data,&data_tuple->value->uint8,56);
      layer_mark_dirty(&layer);
    }
  }

  Tuple *power_tuple = dict_find(iter, GOPRO_POWER_STATUS_KEY);
  if (power_tuple) {
    if(power_tuple->value->uint8 != powerstatus){
      powerstatus = power_tuple->value->uint8;
      layer_mark_dirty(&layer);
    }
  }
}

void pbl_main(void *params) {
  PebbleAppHandlers handlers = {
    .init_handler = &handle_init,
    .deinit_handler = &handle_deinit,
    .messaging_info = {
      .buffer_sizes = {
        .inbound = 64,
        .outbound = 64,
      },
      .default_callbacks.callbacks = {
        .in_received = in_received_handler,
      },
    },
  };
  app_event_loop(params, &handlers);
}
